package com.edee.bigday.search.thread;

import java.util.concurrent.*;

public class ThreadTest {
    //一个系统中应该保证只有一两个线程池，将每个异步任务提交给线程池来执行。
    public static ExecutorService executor = Executors.newFixedThreadPool(100);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main() start00202...");
        /*CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("i=10/2运行结果：" + i);
        }, executor);*/
//        future.join();
        /**
         * 方法执行完成后的感知：
         */
        /*CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程1：" + Thread.currentThread().getId());
            int i = 10 / 0;
            System.out.println("i=10/2运行结果1：" + i);
            return i;
        }, executor).whenComplete((result,exception)->{
            System.out.println("上面任务完成后，又完成了该异步任务！结果是："+result+"，异常是："+exception);
        }).exceptionally(throwable->{
            //whenComplete虽然能感知并获取异常信息，但无法修改异常返回结果。
            //exceptionally可以感知异常，并返回自定义的默认值。
            return 10011;//自定义
        });*/
        /**
         * 方法执行完成后的处理：
         */
        /*CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程1：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("i=10/2运行结果1：" + i);
            return i;
        }, executor).handle((result,exception)->{
            if(result!=null){
                return result+3;//自定义
            }
            if(exception!=null){
                return 0;//自定义
            }
            return 10088;//自定义
        });
        Integer integer = future.get();
        //R apply(T t, U u);*/

        /**
         * 线程串行化：
         * 1、thenRun：不能获取到上一任务的执行结果，无返回值；
         *      thenRunAsync(() -> {
         *             System.out.println("任务2启动！");
         *         },executor);
         *  2、thenAccept：能接收上一任务的结果，无返回值。
         *          thenAcceptAsync(result -> {
         *             //void accept(T t);
         *             System.out.println("任务2启动！"+result);
         *         },executor);
         *  3、thenApply：既能接收上一任务的结果，又有返回值。
         *          thenApplyAsync(result -> {
         *             // R apply(T t);
         *             //void accept(T t);
         *             System.out.println("任务2启动！" + result);
         *             return "Successfully: " + result;
         *         }, executor);
         *         String s = future.get();//阻塞方法
         */
        /*CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程1：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("i=10/2运行结果1：" + i);
            return i;
        }, executor).thenApplyAsync(result -> {
            // R apply(T t);
            //void accept(T t);
            System.out.println("任务2启动！" + result);
            return "Successfully: " + result;
        }, executor);
        String s = future.get();//阻塞方法*/

        /**
         * 组合两个任务：
         */
        //任务一：
        /*CompletableFuture<Object> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务一：当前线程1：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("任务一结束！");
            return i;
        }, executor);

        //任务二：
        CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务二：当前线程1：" + Thread.currentThread().getId());
            int i = 10 / 2;
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务二结束！");
            return "Hello,Task02!";
        }, executor);*/

        //任务三：组合任务一和二，两个任务都完成，才开始任务三：
        /*future01.runAfterBothAsync(future02,()->{
            System.out.println("任务三开始...");
        },executor);*/
        /*future01.thenAcceptBothAsync(future02,(f1,f2)->{
            //BiConsumer：void accept(T t, U u);//只有两个入参，分别是future01和future02的返回值。
            System.out.println("任务3开始，可以获得前两个任务的结果："+f1+"、"+f2);
        },executor);*/

        /*CompletableFuture<String> future03 = future01.thenCombineAsync(future02, (f1, f2) -> {
            //BiFunction<? super T,? super U,? extends V> fn)
            //R apply(T t, U u);
            return f1 + "：" + f2 + "-->说明Task03可以获取前两个future任务的返回结果。";
        }, executor);*/
        /**
         * 前两个任务，只要任意一个完成，就开始执行任务3：
         * 1、runAfterEither：不能获取到前置任务的返回结果，自身也无返回值；
         * 2、acceptEither:可以获取前置任务的返回结果，自身无返回值；
         * 3、applyToEither：可以获取前置任务返回结果，自身也有返回值。
         */
        /*future01.runAfterEitherAsync(future02,()->{
            System.out.println("任务三开始了...");
        },executor);*/
        /*future01.acceptEitherAsync(future02,result->{
            //Consumer<? super T> action,----void accept(T t);只有入参，没有出参。
            //将任务一、二的返回值类型修改为Object
            System.out.println("任务三开始了...,之前任务的返回结果："+result);
        },executor);*/
        /*CompletableFuture<String> future03 =
                future01.applyToEitherAsync(future02, result -> result.toString() + "<--前置任务返回的结果，此时也说明任务三有返回值！", executor);*/
//                    System.out.println("任务三开始。。。前置任务返回结果："+result);
        CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("查询商品图片信息！");
            return "Images!";
        }, executor);

        CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品属性信息~~~");
            return "Attrs属性!";
        }, executor);

        CompletableFuture<String> futureBrand = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品品牌。。。001");
            return "品牌--Brands";
        }, executor);

//        CompletableFuture<Void> allOf = CompletableFuture.allOf(futureImg, futureAttr, futureBrand);
//        allOf.get();//等待所有前置任务完成，运行结果为：
        /*main() start00202...
        查询商品图片信息！
        查询商品属性信息~~~
        查询商品品牌。。。001
        main() end！
        */
//        allOf.join();//同上，运行结果为：
        /*mmain() start00202...
        查询商品图片信息！
        查询商品属性信息~~~
        查询商品品牌。。。001
        main() end！*/
//        System.out.println("main() end！"+"前置任务返回结果："+futureImg.get()+"==>"+futureAttr.get()+"==>"+futureBrand.get());
        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureImg, futureAttr, futureBrand);
        anyOf.get();
        System.out.println("main() end！"+"执行完毕的任务返回值："+anyOf.get());
    }


    public void thread(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main() start001...");
        /**
         * 1）集成Thread类；
         *   Thread01 thread01 = new Thread01();
         *   thread01.start();
         * 2）实现Runnable接口；
         * Runnable01 runnable01 = new Runnable01();
         *   //Thread thread = new Thread(runnable01);
         *   //thread.start();
         *   //或者：
         *  new Thread(runnable01).start();
         * 3）实现Callable接口+FutureTask（可以拿到返回结果、处理异常），JDK1.5及以上版本；
         *     FutureTask<Integer> futureTask = new FutureTask<>(new Callable01());
         *     new Thread(futureTask).start();
         *     //Callable利用FutureTask进行包装，可以"阻塞"等待整个线程执行完成，获取返回结果：
         *     Integer integer = futureTask.get();
         *     System.out.println("main() end！"+integer);
         *
         *     FutureTask还可以接收一个Runnable对象，并使用另一个对象接收返回值（测试对象无法接收到返回值？？）：
         *     BrandVo brandVo1 = new BrandVo();
         *     FutureTask<BrandVo> brandVoFutureTask = new FutureTask<>(new Runnable01(), brandVo1);
         *     new Thread(brandVoFutureTask).start();
         *     BrandVo brandVo = brandVoFutureTask.get();
         *     System.out.println("main() end！"+brandVo);
         *
         * 4）线程池[ExecutorService]；
         *      向线程池中提交任务。
         *   service.execute(new Runnable01());
         *   创建：
         *   4.1）Executors;
         *   4.2）new ThreadPoolExecutor();
         *      七大参数：
         *      int corePoolSize,核心线程数(一直存在，除非allowCoreThreadTimeOut)，线程池创建完毕即准备就绪的、等待异步任务的线程的数量。
         *          Thread thread = new Thread(); thread.start();
         *      int maximumPoolSize,最大线程数，用以控制资源。
         *      long keepAliveTime,(非核心线程)存活时间，如果当前线程数大于核心线程数，只要空闲线程的存活时间大于设定的keepAliveTime，
         *          非核心线程即被释放。
         *      TimeUnit unit,时间单位。
         *      BlockingQueue<Runnable> workQueue,阻塞队列。如果任务数超过核心线程数，未分配线程的任务被放入该阻塞队列。一旦有核心线程空闲出来，
         *          就会从阻塞队列里取出任务执行。
         *      ThreadFactory threadFactory,线程工厂。如，如果对线程的名称等属性有特殊约束，可以通过线程工厂来统一设置。
         *      RejectedExecutionHandler handler.拒绝执行处理程序。当阻塞队列已满时，按照指定的拒绝策略拒绝执行任务。
         *
         *  工作流程：
         *      1）线程池创建，准备好core数量的核心线程，准备接受任务；
         *          1.1）如果core已满，则将再进来的任务放入阻塞队列中，后续空闲出的core就会从阻塞队列中获取任务并执行；
         *          1.2）如果阻塞队列已满，就直接开启新线程来执行任务，能开启的线程数最多为指定的max的数量；
         *          1.3）如果线程数已达到max，则使用拒绝策略拒绝任务；
         *          1.4）如果核心线程之外的线程都空闲出来了，就会在指定的keepAliveTime之后释放线程。
         *       问：一个线程池：core：7，max：20，queue：50。如果有100个并发进来，如何分配？
         *       答：100个并发中的7个会立即执行，50个放入queue。再开启13个线程执行13个任务。剩余的30个使用拒绝策略AbortPolicy予以拒绝。
         *          如果不抛弃而继续执行，可以使用CallerRunsPolicy。也可以使用DiscardOldestPolicy。
         *
         *
         *
         *  区别：1、2不能获取返回值，3可以获取返回值。
         *      1、2、3均无法控制资源。4可以控制资源，保证系统性能稳定。
         */
        //使用线程池的原因：如果后续的业务逻辑中，异步请求特别多，并且每次都是用原生的方法为每个请求开启一个线程，
        // 则最终会将资源耗尽。所以，以后是业务中，以上三种启动线程的方式都不用。应将所有的多线程异步任务都交给线程池。
        //new Thread(()-> System.out.println("Async")).start();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,
                200,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(100000), //最大值默认是Integer的最大值，使用默认值可能会耗尽内存，可以根据压测峰值结果指定最大值，如100000。
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        //可以使用最原生的方式创建，也可以Executors快速创建。Executors中4种常见的线程池：
        //1、newCachedThreadPool：创建一个可缓存线程池，如果线程池长度超过处理需要，可灵活回收空闲线程，若无可回收，则新建线程。
        Executors.newCachedThreadPool();//corePoolSize=0，keepAliveTime=60L，所有都可回收。
        //2、newFixedThreadPool：创建一个定长线程池，可控制线程最大并发数，超出的线程会在队列中等待。
        Executors.newFixedThreadPool(5);//corePoolSize=maximumPoolSize，keepAliveTime=0L，都不可回收。
        //3、newScheduledThreadPool：创建一个定长线程池，支持定时及周期性任务执行。
        Executors.newScheduledThreadPool(5);//可用于执行定时任务。
            /*long triggerTime(long delay) {
                return System.nanoTime() +
                        ((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
            }*/
        //4、newSingleThreadExecutor：//corePoolSize=maximumPoolSize=1，keepAliveTime=0L，不可回收。
        // 创建一个单线程化的线程池，它只会用唯一的工作线程来执行任务，保证所有任务顺序执行。
        Executors.newSingleThreadExecutor();


        System.out.println("main() end！");
    }


    //方式1：集成Thread：
    public static class Thread01 extends Thread {
        @Override
        public void run() {
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("10/2运行结果：" + i);
        }
    }

    //方式2：实现Runnable接口：
    public static class Runnable01 implements Runnable {
        @Override
        public void run() {
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("i=10/2运行结果：" + i);
        }
    }

    //方式3：实现Callable接口+FutureTask：
    public static class Callable01 implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("i=10/2运行结果：" + i);
            return i;
        }
    }
}
